#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>

#include <string.h>

int
socket_bind_listen(sa_family_t family, in_port_t port, int backlog,
	int v6only)
{
	int fd;
	struct sockaddr_storage addr;
	socklen_t slen;

	memset(&addr, 0, sizeof(addr));

	switch (family) {
	case AF_INET:
		{
			struct sockaddr_in *p =
				(struct sockaddr_in *)&addr;
			slen = sizeof(*p);
			p->sin_port = htons(port);
			p->sin_addr.s_addr = INADDR_ANY;
		}
		break;
	case AF_INET6:
		{
			struct sockaddr_in6 *p =
				(struct sockaddr_in6 *)&addr;
			slen = sizeof(*p);
			p->sin6_port = htons(port);
			p->sin6_addr = in6addr_any;
		}
		break;
	default:
		fd = -1;
		//errno = EAFNOSUPPORT;
		goto DONE;
	}

	addr.ss_family = family;

	if ((fd = socket(family, SOCK_STREAM, 0)) == -1)
		goto DONE;

	int on = -1;
	if (setsockopt(fd, SOL_SOCKET, SO_REUSEADDR, &on,
			sizeof(on)) == -1)
		goto ERROR;
	if (v6only && family == AF_INET6) {
		on = -1;
		if (setsockopt(fd, IPPROTO_IPV6, IPV6_V6ONLY, &on,
				sizeof(on)) == -1)
			goto ERROR;
	}
	if (bind(fd, (struct sockaddr *)&addr, slen) == -1)
		goto ERROR;
	if (listen(fd, backlog) == -1)
		goto ERROR;

DONE:
	return fd;
ERROR:
	close(fd);
	fd = -1;
	goto DONE;
}
